﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Warlock.Input
{
    /// <summary>
    /// Haelt den Status der aktuellen und letzten Keyboard- und Mousestates nach. Bietet außerdem Events an, um auf Mausklicks etc. zu reagiern.
    /// Es muss lediglich in der Update() Methode einmal InputManager.Input.Update() aufgerufen werden.
    /// Achtung: Singleton! Kann ueber InputManager.Input aufgerufen werden.
    /// </summary>
    public class InputManager
    {
        //=================================================
        //Singleton
        //=================================================
        private static InputManager _input;
        public static InputManager Instance
        {
            get
            {
                if (_input == null)
                    _input = new InputManager();
                return _input;
            }
            set
            {
                _input = value;
            }
        }
        //=================================================

        public KeyboardState CurrentKeyboardState { get; private set; }
        public MouseState CurrentMouseState { get; private set; }
        public KeyboardState PreviousKeyboardState { get; private set; }
        public MouseState PreviousMouseState { get; private set; }

        //Maus
        public event MouseClickEventHandler MouseDown;
        public event MouseClickEventHandler MouseUp;
        public event MouseMovedEventHandler MouseMoved;
        //Keyboard
        public event KeyDownEventHandler KeyDown;
        public event KeyUpEventHandler KeyUp;

        //Konstruktor
        private InputManager()
        {
        }

        //Maus
        private void OnMouseDown(MouseButton mouseButton)
        {
            if (MouseDown != null)
                MouseDown(new MouseClickEventArgs(CurrentMouseState, PreviousMouseState, mouseButton, new Vector2(CurrentMouseState.X, CurrentMouseState.Y)));
        }
        private void OnMouseUp(MouseButton mouseButton)
        {
            if (MouseUp != null)
                MouseUp(new MouseClickEventArgs(CurrentMouseState, PreviousMouseState, mouseButton, new Vector2(CurrentMouseState.X, CurrentMouseState.Y)));
        }
        private void OnMouseMoved()
        {
            if (MouseMoved != null)
                MouseMoved(new MouseMovedEventArgs(new Vector2(PreviousMouseState.X, PreviousMouseState.Y),
                    new Vector2(CurrentMouseState.X, CurrentMouseState.Y), CurrentMouseState));
        }
        //Keyboard
        private void OnKeyDown(Keys key)
        {
            if (KeyDown != null)
                KeyDown(new KeyPressedEventArgs(key, CurrentKeyboardState.GetPressedKeys().ToList<Keys>()));
        }
        private void OnKeyUp(Keys key)
        {
            if (KeyUp != null)
                KeyUp(new KeyPressedEventArgs(key, CurrentKeyboardState.GetPressedKeys().ToList<Keys>()));
        }

        /// <summary>
        /// Muss in jedem Update Aufruf des Spiels, aufgerufen werden.
        /// </summary>
        public void Update()
        {
            CurrentKeyboardState = Keyboard.GetState();
            CurrentMouseState = Mouse.GetState();

            // Ueberpruefen, ob Events gefeuert werden muessen
            //MouseMoved
            if ((CurrentMouseState.X != PreviousMouseState.X) || (CurrentMouseState.Y != PreviousMouseState.Y))
                OnMouseMoved();
            //MouseDown
            if (CurrentMouseState.LeftButton == ButtonState.Pressed && PreviousMouseState.LeftButton == ButtonState.Released)
                OnMouseDown(MouseButton.Left);
            if (CurrentMouseState.RightButton == ButtonState.Pressed && PreviousMouseState.RightButton == ButtonState.Released)
                OnMouseDown(MouseButton.Right);
            if (CurrentMouseState.MiddleButton == ButtonState.Pressed && PreviousMouseState.MiddleButton == ButtonState.Released)
                OnMouseDown(MouseButton.Middle);
            //MouseUp
            if (PreviousMouseState.LeftButton == ButtonState.Pressed && CurrentMouseState.LeftButton == ButtonState.Released)
                OnMouseUp(MouseButton.Left);
            if (PreviousMouseState.RightButton == ButtonState.Pressed && CurrentMouseState.RightButton == ButtonState.Released)
                OnMouseUp(MouseButton.Right);
            if (PreviousMouseState.MiddleButton == ButtonState.Pressed && CurrentMouseState.MiddleButton == ButtonState.Released)
                OnMouseUp(MouseButton.Middle);
            //KeyPressed
            List<Keys> pressedKeys = CurrentKeyboardState.GetPressedKeys().ToList<Keys>();
            List<Keys> previousPressedKeys = PreviousKeyboardState.GetPressedKeys().ToList<Keys>();


            // ! Eventuell Performance kritisch !
            //Die Keys, die im letzten Update noch nicht gedrueckt waren
            List<Keys> newPressedKeys = new List<Keys>();
            for (int i = 0; i < pressedKeys.Count; i++)
                if (!previousPressedKeys.Contains(pressedKeys[i]))
                    newPressedKeys.Add(pressedKeys[i]);
            //Die Keys, die losgelassen wurden
            List<Keys> newReleasedKeys = new List<Keys>();
            for (int i = 0; i < previousPressedKeys.Count; i++)
                if (!pressedKeys.Contains(previousPressedKeys[i]))
                    newReleasedKeys.Add(previousPressedKeys[i]);
            if (newPressedKeys.Count > 0)
                foreach (Keys key in newPressedKeys)
                    OnKeyDown(key);
            if (newReleasedKeys.Count > 0)
                foreach (Keys key in newReleasedKeys)
                    OnKeyUp(key);

            PreviousKeyboardState = CurrentKeyboardState;
            PreviousMouseState = CurrentMouseState;
        }
    }
}